# OdbCmake - CMake find module for ODB
#
# Copyright (c) 2014 Timo Rothenpieler <btbn@btbn.de>,
# Per Edin <info@peredin.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

set(ODB_COMPILE_DEBUG TRUE)
set(ODB_COMPILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/odb_gen")
set(ODB_COMPILE_HEADER_SUFFIX ".hpp")
set(ODB_COMPILE_INLINE_SUFFIX "_inline.hpp")
set(ODB_COMPILE_SOURCE_SUFFIX ".cpp")
set(ODB_COMPILE_FILE_SUFFIX "_odb")
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)

function(odb_compile outvar)

  if(NOT ODB_EXECUTABLE)
    message(FATAL_ERROR "odb compiler executable not found")
  endif()

  set(options GENERATE_QUERY GENERATE_SESSION GENERATE_SCHEMA GENERATE_PREPARED)
  set(oneValueParams SCHEMA_FORMAT SCHEMA_NAME TABLE_PREFIX EPILOGUE
      STANDARD SLOC_LIMIT DEFAULT_POINTER
      HEADER_PROLOGUE INLINE_PROLOGUE SOURCE_PROLOGUE
      HEADER_EPILOGUE INLINE_EPILOGUE SOURCE_EPILOGUE
      MULTI_DATABASE X_CPP_OPTION CHANGELOG_DIR)
  set(multiValueParams FILES INCLUDE DB PROFILE)

  cmake_parse_arguments(PARAM "${options}" "${oneValueParams}" "${multiValueParams}" ${ARGN})

  if(PARAM_UNPARSED_ARGUMENTS)
    message(FATAL_ERROR "invalid arguments passed to odb_wrap_cpp: ${PARAM_UNPARSED_ARGUMENTS}")
  endif()

  if(NOT PARAM_FILES)
    message(FATAL_ERROR: "no input files to odb_compile")
  endif()

  set(ODB_ARGS)

  if(PARAM_MULTI_DATABASE)
    list(APPEND ODB_ARGS --multi-database "${PARAM_MULTI_DATABASE}")
    list(APPEND PARAM_DB common)
  endif()

  foreach(db ${PARAM_DB})
    list(APPEND ODB_ARGS -d "${db}")
  endforeach()

  if(PARAM_GENERATE_QUERY)
    list(APPEND ODB_ARGS --generate-query)
  endif()

  if(PARAM_DEFAULT_POINTER)
    list(APPEND ODB_ARGS --default-pointer "${DEFAULT_POINTER}")
  endif()

  if(PARAM_GENERATE_SESSION)
    list(APPEND ODB_ARGS --generate-session)
  endif()

  if(PARAM_GENERATE_SCHEMA)
    list(APPEND ODB_ARGS --generate-schema)
  endif()

  if(PARAM_GENERATE_PREPARED)
    list(APPEND ODB_ARGS --generate-prepared)
  endif()

  if(PARAM_EPILOGUE)
    list(APPEND ODB_ARGS --odb-epilogue "${PARAM_EPILOGUE}")
  endif()

  if(PARAM_SCHEMA_FORMAT)
    list(APPEND ODB_ARGS --schema-format "${PARAM_SCHEMA_FORMAT}")
  endif()

  if(PARAM_SCHEMA_NAME)
    list(APPEND ODB_ARGS --schema-name "${PARAM_SCHEMA_NAME}")
  endif()

  if(PARAM_TABLE_PREFIX)
    list(APPEND ODB_ARGS --table-prefix "${PARAM_TABLE_PREFIX}")
  endif()

  if(PARAM_STANDARD)
    list(APPEND ODB_ARGS --std "${PARAM_STANDARD}")
  endif()

  if(PARAM_SLOC_LIMIT)
    list(APPEND ODB_ARGS --sloc-limit "${PARAM_SLOC_LIMIT}")
  endif()

  if(PARAM_HEADER_PROLOGUE)
    list(APPEND ODB_ARGS --hxx-prologue-file "${PARAM_HEADER_PROLOGUE}")
  endif()

  if(PARAM_INLINE_PROLOGUE)
    list(APPEND ODB_ARGS --ixx-prologue-file "${PARAM_INLINE_PROLOGUE}")
  endif()

  if(PARAM_SOURCE_PROLOGUE)
    list(APPEND ODB_ARGS --cxx-prologue-file "${PARAM_SOURCE_PROLOGUE}")
  endif()

  if(PARAM_HEADER_EPILOGUE)
    list(APPEND ODB_ARGS --hxx-epilogue-file "${PARAM_HEADER_EPILOGUE}")
  endif()

  if(PARAM_INLINE_EPILOGUE)
    list(APPEND ODB_ARGS --ixx-epilogue-file "${PARAM_INLINE_EPILOGUE}")
  endif()

  if(PARAM_SOURCE_EPILOGUE)
    list(APPEND ODB_ARGS --cxx-epilogue-file "${PARAM_SOURCE_EPILOGUE}")
  endif()

  if(PARAM_CHANGELOG_DIR)
    list(APPEND ODB_ARGS --changelog-dir "${PARAM_CHANGELOG_DIR}")
  endif()

  foreach(profile ${PARAM_PROFILE})
    list(APPEND ODB_ARGS --profile "${profile}")
  endforeach()

  if(PARAM_X_CPP_OPTION)
    list(APPEND ODB_ARGS -x "${PARAM_X_CPP_OPTION}")
  endif()

  list(APPEND ODB_ARGS --output-dir "${ODB_COMPILE_OUTPUT_DIR}")
  list(APPEND ODB_ARGS --hxx-suffix "${ODB_COMPILE_HEADER_SUFFIX}")
  list(APPEND ODB_ARGS --ixx-suffix "${ODB_COMPILE_INLINE_SUFFIX}")
  list(APPEND ODB_ARGS --cxx-suffix "${ODB_COMPILE_SOURCE_SUFFIX}")

  if(PARAM_MULTI_DATABASE AND NOT "${ODB_COMPILE_FILE_SUFFIX}" MATCHES ".+:.+")
    set(osuffix "${ODB_COMPILE_FILE_SUFFIX}")
    set(ODB_COMPILE_FILE_SUFFIX)
    foreach(db ${PARAM_DB})
      if("${db}" MATCHES "common")
        list(APPEND ODB_COMPILE_FILE_SUFFIX "${db}:${osuffix}")
      else()
        list(APPEND ODB_COMPILE_FILE_SUFFIX "${db}:${osuffix}_${db}")
      endif()
    endforeach()
  endif()

  foreach(sfx ${ODB_COMPILE_FILE_SUFFIX})
    list(APPEND ODB_ARGS --odb-file-suffix "${sfx}")
  endforeach()

  foreach(dir ${PARAM_INCLUDE} ${ODB_INCLUDE_DIRS})
    list(APPEND ODB_ARGS "-I${dir}")
  endforeach()

  file(REMOVE_RECURSE "${ODB_COMPILE_OUTPUT_DIR}")
  file(MAKE_DIRECTORY "${ODB_COMPILE_OUTPUT_DIR}")

  foreach(input ${PARAM_FILES})
    get_filename_component(fname "${input}" NAME_WE)
    set(outputs)

    foreach(sfx ${ODB_COMPILE_FILE_SUFFIX})
      string(REGEX REPLACE ":.*$" "" pfx "${sfx}")
      string(REGEX REPLACE "^.*:" "" sfx "${sfx}")

      if(NOT "${PARAM_MULTI_DATABASE}" MATCHES "static" OR NOT "${pfx}" MATCHES "common")
        set(output "${ODB_COMPILE_OUTPUT_DIR}/${fname}${sfx}${ODB_COMPILE_SOURCE_SUFFIX}")
        list(APPEND ${outvar} "${output}")
        list(APPEND outputs "${output}")
        message(STATUS "${outputs}")
      endif()
    endforeach()

    if(ODB_COMPILE_DEBUG)
      set(_msg "${ODB_EXECUTABLE} ${ODB_ARGS} ${input}")
      string(REPLACE ";" " " _msg "${_msg}")
      message(STATUS "${_msg}")
    endif()

    # Look for libraries installed with the executable:
    # e.g. if the binary is in /opt/odb/bin, look for libraries in /opt/odb/lib
    get_filename_component(ODB_BIN_PATH ${ODB_EXECUTABLE} DIRECTORY)
    get_filename_component(ODB_LD_LIBRARY_PATH "${ODB_BIN_PATH}/../lib" ABSOLUTE)

    get_filename_component(target_suffix "${input}" NAME)
    add_custom_target("generate_odb_${target_suffix}"
        COMMAND "LD_LIBRARY_PATH=${ODB_LD_LIBRARY_PATH}:${ODB_LIBRARY_PATH}:${LD_LIBRARY_PATH}" ${ODB_EXECUTABLE} ${ODB_ARGS} "${input}"
        DEPENDS "${input}"
        BYPRODUCTS ${outputs}
        WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
        VERBATIM)

  endforeach()

  set(${outvar} ${${outvar}} PARENT_SCOPE)
endfunction()
